package com.bm.service.Impl;

import com.bm.enums.ItemType;
import com.bm.enums.OrderStatus;
import com.bm.exception.BusinessException;
import com.bm.exception.ExceptionCode;
import com.bm.model.Item;
import com.bm.model.Order;
import com.bm.service.IOrderService;
import com.bm.util.ItemUtil;
import com.bm.util.LocalDateTimeUtil;
import com.google.common.util.concurrent.AtomicDouble;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单服务基础实现
 */
public class BaseOrderServiceImpl implements IOrderService {
    //protected final OrderRepository repository;
    protected final Clock systemClock; // 可用于真实环境时间获取

    // 订单缓存
    // key: orderId, value: Order
    private Map<String, Order> orderCache = new HashMap<>();

    public BaseOrderServiceImpl() {
        //this.repository = repository;
        this.systemClock = Clock.systemDefaultZone();
    }

    // 真实环境使用的创建方法（自动获取当前时间）
    @Override
    public Order createOrder(List<Item> items, boolean isVIP, LocalDateTime currentTime) {
        if (currentTime == null) {
            // 如果未指定模拟当前时间, 则使用系统真实当前时间
            currentTime = LocalDateTime.now();
        }
        // 判断订单不能为空
        validEmptyOrder(items);
        /*
        → 是 → [是否有甜品且无主食?]
                  → 是 → 抛出"甜品不能单独购买"异常 → 结束
         */
        // 判断,不能单独购买甜品
        validDessert(items);
        /*
        → 非空 → [是否包含主食?]
          → 否 → 抛出"必须包含主食"异常 → 结束
         */
        // 判断必须包含主食
        validMainFood(items);
        // 构建返回订单对象
        Order order = new Order();

        AtomicDouble totalPrice = new AtomicDouble(0);

        // test01
        // 输入：1个冰淇淋
        // 预期：抛出BusinessException，错误码"DESSERT_ALONE"
        // 统计甜品数量
        long countDessert = items.stream().filter(item -> ItemType.DESSERT.equals(item.getType())).count();
        // 统计主食数量
        long countMain = items.stream().filter(item -> ItemType.MAIN.equals(item.getType())).count();
        // 统计饮料数量
        long countDrink = items.stream().filter(item -> ItemType.DRINK.equals(item.getType())).count();
        if (countMain == 0 && countDrink == 0 && countDessert > 0) {
            // 只点了甜品
            throw new BusinessException(ExceptionCode.DESSERT_ALONE);
        }


        // test02
        // 输入：汉堡 + 薯条
        // 预期：总价=25元（原价15+15=30）
        Set<String> itemIds = items.stream().map(Item::getId).collect(Collectors.toSet());
        // 套餐1: 汉堡+薯条
        List<String> package1 = Arrays.asList("H001", "F002");
        if (itemIds.containsAll(package1)) {
            // 套餐1
            // 总价的基础上减5元
            //  遗漏bug修复: 多份套餐, 需要减多份套餐的优惠价
            // 统计套餐汉堡数量
            long countHamburger = items.stream().filter(item -> "H001".equals(item.getId())).count();
            // 统计套餐薯条数量
            long countFries = items.stream().filter(item -> "F002".equals(item.getId())).count();
            // 计算买了几份套餐1
            long countPackage1 = Math.min(countHamburger, countFries);
            // 计算套餐1总共优惠的价格
            double discountPrice = countPackage1 * -5;
            totalPrice.addAndGet(discountPrice);
        }

        /*
        → 否 → [检查冰咖啡时间（15点后禁止）?]
                          → 是 → 移除冰咖啡并添加提示 → 继续
         */
        // test03
        // 当前时间：16:00
        // 输入：冰咖啡 + 汉堡
        // 预期：冰咖啡被移除，总价=15元，订单备注"冰咖啡已下架"
        // [检查冰咖啡时间（15点后禁止）?]
        if (countDrink > 0) {
            if (currentTime.getHour() >= 15) {
                // 判断是否有冰咖啡, 有就移除, 并添加订单备注
                String iceId = "C004";
                List<Item> iceList = items.stream().filter(item -> iceId.equals(item.getId())).collect(Collectors.toList());
                if (!iceList.isEmpty()) {
                    // 移除所有冰咖啡
                    items.removeAll(iceList);
                    // 追加订单备注
                    order.appendRemark("冰咖啡已下架");
                }
            }
        }

        /*
        → 否 → [检查是否有两杯同款饮料?]
        → 是 → 第二杯半价 → 继续
         */
        // 饮料第二杯半价优惠
        //// 在此处将所有饮料类的商品价格计算完, 后面不再计算饮料类的价格
        if (countDrink > 0) {
            // 饮料第二杯半价优惠
            // 同款饮料每2杯,有1杯半价
            List<Item> drinkList = items.stream().filter(item -> ItemType.DRINK.equals(item.getType())).collect(Collectors.toList());
            // 对饮料不同款进行统计
            // key: itemId, value: itemCount
            Map<String, Long> groupAndCount = drinkList.stream()
                    .collect(Collectors.groupingBy(Item::getId, Collectors.counting()));
            groupAndCount.forEach((itemId, count) -> {
                // 计算满足几次半价优惠
                long discountTimes = count / 2;
                // 剩余未满足优惠的饮料
                long leftItems = count % 2;
                // 获取商品信息和原始价格
                Item itemInfo = ItemUtil.getItemInfo(itemId);
                double itemPrice = itemInfo.getPrice();
                // 计算饮品会优惠多少钱
                double discountPrice = discountTimes * itemPrice;
                // 总价减去饮品优惠的价格, 剩下的饮品价格在最后统计累加
                // 最终此款类别的饮料价格总计 (优惠商品价格 + 未优惠商品价格)
                double totalDrinkPrice = discountTimes * 1.5 * itemPrice + leftItems * itemPrice;
                // 将此款饮料计入总价
                totalPrice.set(totalPrice.get() + totalDrinkPrice);
            });
        }

        // 计算所有商品的总价格
        items.stream()
                // 过滤掉饮品, 前面已计算所有饮品的价格
                .filter(item -> !ItemType.DRINK.equals(item.getType()))
                // 累加商品的价格
                .forEach(item -> totalPrice.addAndGet(item.getPrice()));

        /*
        → 否 → [总价>100且非周末?]
                → 是 → 满减10元 → 继续
         */
        // 非周末的时候, 有满减活动, 满100减10元
        // 获取今天的星期, from 1 (Monday) to 7 (Sunday)
        int dayOfWeek = currentTime.getDayOfWeek().getValue();
        if (dayOfWeek != 7) {
            // 检测满减条件
            if (totalPrice.get() >= 100) {
                // 满足满减活动, 总价减10元
                totalPrice.set(totalPrice.get() - 10);
            }
        }

        // test04
        // 输入：汉堡(15) + 蛋糕(12)（VIP用户）
        // 预期：总价=(15+12)×0.9=24.3元
        if (isVIP) {
            // vip总价打9折
            totalPrice.set(totalPrice.get() * 0.9);
        }

        // test05
        // 输入：汉堡 + 4个鸡块 + 薯条（总计6件商品）
        // 预期：抛出"TOO_MANY_ITEMS"异常
        int itemCount = items.size();
        if (itemCount > 5) {
            throw new BusinessException(ExceptionCode.TOO_MANY_ITEMS);
        }

        // 再次验证订单不能为空
        validEmptyOrder(items);

        order.setItems(items);
        order.setStatus(OrderStatus.CREATED);
        order.setTotalPrice(totalPrice.get());

        String orderId = UUID.randomUUID().toString();
        order.setOrderId(orderId);
        order.setCreateTime(currentTime);
        orderCache.put(orderId, order);
        return order;
    }



    /**
     * 验证订单甜品不能单独购买
     *
     * @param items 商品列表
     */
    private void validDessert(List<Item> items) {
        long countDessert = items.stream().filter(item -> ItemType.DESSERT.equals(item.getType())).count();
        long countMainFood = items.stream().filter(item -> ItemType.MAIN.equals(item.getType())).count();
        if (countDessert > 0 && countMainFood == 0) {
            throw new BusinessException(ExceptionCode.DESSERT_ALONE);
        }
    }

    /**
     * 验证订单必须包含主食
     *
     * @param items 商品列表
     */
    private void validMainFood(List<Item> items) {
        long countMainFood = items.stream().filter(item -> ItemType.MAIN.equals(item.getType())).count();
        if (countMainFood == 0) {
            throw new BusinessException(ExceptionCode.NO_MAIN_FOOD);
        }
    }

    /**
     * 验证订单商品不能为空
     *
     * @param items 商品列表
     */
    private void validEmptyOrder(List<Item> items) {
        if (items == null || items.isEmpty()) {
            throw new BusinessException(ExceptionCode.EMPTY);
        }
    }

    @Override
    public void cancelOrder(String orderId, LocalDateTime currentTime) throws BusinessException {

    }

    @Override
    public Order findOrder(String orderId, LocalDateTime currentTime) {
        if (currentTime == null) {
            currentTime = LocalDateTime.now();
        }
        Order order = orderCache.get(orderId);
        // 如果查询到订单, 检测订单时效, 不超过半小时
        // 已创建的订单, 没有付款, 需要判断订单时效
        if (order != null && OrderStatus.CREATED.equals(order.getStatus())) {
            LocalDateTime createTime = order.getCreateTime();
            long orderTimeStamp = LocalDateTimeUtil.getTimestamp(createTime);
            long sysCurrentTimeStamp = LocalDateTimeUtil.getTimestamp(currentTime);
            // 计算订单已过时间(当前时间-订单创建时间)
            long pastTimeMs = sysCurrentTimeStamp - orderTimeStamp;
            long pastMinutes = TimeUnit.MILLISECONDS.toMinutes(pastTimeMs);
            if (pastMinutes >= 30) {
                // 订单已超期, 更改状态为: "取消"
                order.setStatus(OrderStatus.CANCELLED);
                // update 更新回数据库
                orderCache.put(orderId, order);
            }
        }
        return order;
    }

    @Override
    public List<Order> listOrder() {
        return new ArrayList<>(orderCache.values());
    }

}